package org.jeecg.modules.outOrder.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.constant.WarehouseConstant;
import org.jeecg.modules.basicCompartment.entity.BasicCompartment;
import org.jeecg.modules.basicCompartment.service.IBasicCompartmentService;
import org.jeecg.modules.basicShelf.entity.BasicShelf;
import org.jeecg.modules.basicShelf.service.IBasicShelfService;
import org.jeecg.modules.basicShelfType.entity.BasicShelfType;
import org.jeecg.modules.basicShelfType.service.IBasicShelfTypeService;
import org.jeecg.modules.basicSkuInfo.entity.BasicSkuInfo;
import org.jeecg.modules.basicSkuInfo.service.IBasicSkuInfoService;
import org.jeecg.modules.basicStationInfo.entity.BasicStationInfo;
import org.jeecg.modules.basicStationInfo.service.IBasicStationInfoService;
import org.jeecg.modules.inOrder.entity.InOrder;
import org.jeecg.modules.inOrder.entity.ShelfPTO;
import org.jeecg.modules.inventory.entity.InventoryInfo;
import org.jeecg.modules.inventory.service.IInventoryInfoService;
import org.jeecg.modules.outOrder.entity.OutOrder;
import org.jeecg.modules.outOrder.entity.OutOrderDetail;
import org.jeecg.modules.outOrder.service.IOutOrderDetailService;
import org.jeecg.modules.outOrder.service.IOutOrderService;
import org.jeecg.modules.outOrder.vo.OutOrderPage;
import org.jeecg.modules.outOrderRecord.entity.OutOrderRecord;
import org.jeecg.modules.outOrderRecord.service.IOutOrderRecordService;
import org.jeecg.modules.outOrderTaskMain.entity.OutOrderTaskDetail;
import org.jeecg.modules.outOrderTaskMain.entity.OutOrderTaskMain;
import org.jeecg.modules.outOrderTaskMain.service.IOutOrderTaskDetailService;
import org.jeecg.modules.outOrderTaskMain.service.IOutOrderTaskMainService;
import org.jeecg.modules.reportLabel.entity.ReportLabel;
import org.jeecg.modules.reportLabel.service.IReportLabelService;
import org.jeecg.modules.shelfTask.entity.ShelfTask;
import org.jeecg.modules.shelfTask.service.IShelfTaskService;
import org.jeecg.modules.system.entity.SysRole;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.util.RMSUtil;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 出库单
 * @Author: jeecg-boot
 * @Date: 2023-12-04
 * @Version: V1.0
 */
@Api(tags = "出库管理")
@RestController
@RequestMapping("/outOrder/outOrder")
@Slf4j
public class OutOrderController {
    @Autowired
    private RMSUtil rmsUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private IShelfTaskService shelfTaskService;
    @Autowired
    private IOutOrderService outOrderService;
    @Autowired
    private IOutOrderDetailService outOrderDetailService;
    @Autowired
    private IBasicStationInfoService stationService;
    @Autowired
    private IBasicSkuInfoService basicSkuInfoService;
    @Autowired
    private IBasicShelfService shelfService;
    @Autowired
    private IBasicShelfTypeService shelfTypeService;
    @Autowired
    private IBasicCompartmentService basicCompartmentService;
    @Autowired
    private IOutOrderTaskMainService outOrderTaskMainService;
    @Autowired
    private IOutOrderTaskDetailService outOrderTaskDetailService;
    @Autowired
    private IOutOrderRecordService outOrderRecordService;
    @Autowired
    private IReportLabelService reportLabelService;
    @Autowired
    private IInventoryInfoService inventoryInfoService;

    /**
     * 分页列表查询
     *
     * @param outOrder
     * @param pageNo
     * @param pageSize
     * @param req
     * @return
     */
    @AutoLog(value = "出库管理-分页列表查询")
    @ApiOperation(value = "出库管理-分页列表查询", notes = "出库管理-分页列表查询")
    @GetMapping(value = "/list")
    public Result<?> queryPageList(OutOrder outOrder,
                                   @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                   @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                   HttpServletRequest req) {
        // 获取当前登录人角色
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        SysRole sysRole = sysUserService.selectLoginUserRole();

        QueryWrapper<OutOrder> queryWrapper = QueryGenerator.initQueryWrapper(outOrder, req.getParameterMap());
        // 按照当前登录人角色筛选列表数据
        if (WarehouseConstant.RoleCodeEnum.TALLY.getCode().equals(sysRole.getRoleCode())) {
            queryWrapper.ne("status", WarehouseConstant.OutWarehouseEnum.CREATE_END.getCode());
        } else if (WarehouseConstant.RoleCodeEnum.DRIVER.getCode().equals(sysRole.getRoleCode())) {
            queryWrapper.in("execute_staff", Arrays.asList(WarehouseConstant.OutWarehouseTaskStaffEnum.DRIVER.getCode(), WarehouseConstant.OutWarehouseTaskStaffEnum.TALLY_DRIVER.getCode()));
        }
        queryWrapper.ne("status", WarehouseConstant.OutWarehouseEnum.DELETE.getCode());
        queryWrapper.orderByAsc("FIELD(status, '03', '04', '54', '05', '55', '02', '01', '06', '56')");
        queryWrapper.orderByDesc("create_time");
        Page<OutOrder> page = new Page<OutOrder>(pageNo, pageSize);
        IPage<OutOrder> pageList = outOrderService.page(page, queryWrapper);

        List<BasicStationInfo> stationInfoList = stationService.list();
        List<OutOrder> records = pageList.getRecords();
        for (OutOrder order : records) {
            // 封装工作站名称
            List<BasicStationInfo> stationList = stationInfoList.stream().filter(data -> data.getStationCode().equals(order.getStationCode())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(stationList)) {
                BasicStationInfo basicStationInfo = stationList.get(0);
                order.setStationName(basicStationInfo.getStationName());
            }
            // 封装入库任务状态
            if (Objects.equals(WarehouseConstant.RoleCodeEnum.TALLY.getCode(), sysRole.getRoleCode())) {
                OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                        .eq(OutOrderTaskMain::getOutOrderId, order.getId())
                        .eq(OutOrderTaskMain::getExecuteStaff, WarehouseConstant.EnterWarehouseTaskStaffEnum.TALLY.getCode()));
                order.setTaskStatus(null == outOrderTaskMain ? order.getStatus() : outOrderTaskMain.getStatus());
            } else if (Objects.equals(WarehouseConstant.RoleCodeEnum.DRIVER.getCode(), sysRole.getRoleCode())) {
                OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                        .eq(OutOrderTaskMain::getOutOrderId, order.getId())
                        .eq(OutOrderTaskMain::getExecuteStaff, WarehouseConstant.EnterWarehouseTaskStaffEnum.DRIVER.getCode()));
                order.setTaskStatus(null == outOrderTaskMain ? order.getStatus() : outOrderTaskMain.getStatus());
            } else {
                order.setTaskStatus(order.getStatus());
            }

        }
        pageList.setRecords(records);

        return Result.OK(pageList);
    }

    /**
     * 添加
     *
     * @param outOrderPage 出库单对象
     * @return
     */
    @AutoLog(value = "出库管理-添加出库单")
    @ApiOperation(value = "出库管理-添加出库单", notes = "出库管理-添加出库单")
    @PostMapping(value = "/add")
    public Result<?> add(@RequestBody OutOrderPage outOrderPage) {
        // 字段校验
        this.checkColumns(outOrderPage);

        OutOrder outOrder = new OutOrder();
        BeanUtils.copyProperties(outOrderPage, outOrder);
        outOrderService.saveWarehousingOut(outOrder, outOrderPage.getOutOrderDetailList());
        return Result.OK();
    }

    /**
     * 编辑
     *
     * @param outOrderPage 出库单对象
     * @return
     */
    @AutoLog(value = "出库管理-编辑出库单")
    @ApiOperation(value = "出库管理-编辑出库单", notes = "出库管理-编辑出库单")
    @PutMapping(value = "/edit")
    public Result<?> edit(@RequestBody OutOrderPage outOrderPage) {
        // 字段校验
        Assert.isTrue(outOrderPage.getStatus().equals(WarehouseConstant.OutWarehouseEnum.CREATE_END.getCode()), "已发布的出库单不支持修改，请联系管理员！");
        this.checkColumns(outOrderPage);

        OutOrder outOrder = new OutOrder();
        BeanUtils.copyProperties(outOrderPage, outOrder);
        outOrderService.saveWarehousingOut(outOrder, outOrderPage.getOutOrderDetailList());
        return Result.OK();
    }

    /**
     * 字段校验
     *
     * @param outOrderPage
     */
    private void checkColumns(OutOrderPage outOrderPage) {
        Assert.notNull(outOrderPage.getOutOrderTime(), "请选择出库时间！");
        Assert.notNull(outOrderPage.getGoodsType(), "请选择品番类型！");
        Assert.notEmpty(outOrderPage.getOutOrderDetailList(), "请填写出库明细！");
        long nullCount = outOrderPage.getOutOrderDetailList().stream().filter(data -> StringUtils.isBlank(data.getSkuCode()) || data.getBoxNumber() == null || data.getGoodsNumber() == null).count();
        Assert.isTrue(nullCount == 0, "品番编码、出库箱数与出库件数必须填写！");

        QueryWrapper<OutOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne(StringUtils.isNotBlank(outOrderPage.getId()), "id", outOrderPage.getId());
        queryWrapper.eq("out_order_code", outOrderPage.getOutOrderCode());
        int repeatCount = outOrderService.count(queryWrapper);
        Assert.isTrue(repeatCount == 0, "订单号已存在！");
    }

    @AutoLog(value = "出库管理-发布出库单")
    @ApiOperation(value = "出库管理-发布出库单", notes = "编辑出库单，补充出库信息后发布，才能领取")
    @PostMapping("/release")
    public Result<?> release(@RequestParam String id) {
        outOrderService.releaseWarehousingOut(id);
        return Result.OK();
    }

    @AutoLog(value = "出库管理-领取任务")
    @ApiOperation(value = "出库管理-领取任务", notes = "出库管理-领取任务")
    @PostMapping("/collect")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> collect(String id, String stationCode) {
        Assert.isTrue(StringUtils.isNotBlank(id), "请选择要领取的出库单任务！");
        // 获取当前登录人角色
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        SysRole sysRole = sysUserService.selectLoginUserRole();
        Assert.isTrue(Arrays.asList(WarehouseConstant.RoleCodeEnum.TALLY.getCode(), WarehouseConstant.RoleCodeEnum.DRIVER.getCode()).contains(sysRole.getRoleCode()), "您既不是理货员也不是叉车司机，无法领取任务！");

        // 校验当前登录人是否可领取入库单
        OutOrder outOrder = outOrderService.getById(id);
        OutOrderTaskMain outOrderTaskMain = new OutOrderTaskMain();
        if (WarehouseConstant.RoleCodeEnum.TALLY.getCode().equals(sysRole.getRoleCode())) {
            Assert.isTrue(StringUtils.isNotBlank(stationCode), "请选择工作站后领取任务！");
            Assert.isTrue(Arrays.asList(WarehouseConstant.EnterWarehouseTaskStaffEnum.TALLY.getCode(), WarehouseConstant.OutWarehouseTaskStaffEnum.TALLY_DRIVER.getCode()).contains(outOrder.getExecuteStaff()),
                    "当前出库单无需AGV出库！");

            outOrder.setTally(loginUser.getUsername());
            outOrder.setStationCode(stationCode);
            outOrder.setTallyReceive(new Date());

            outOrderTaskMain = outOrderTaskMainService.getOne(new QueryWrapper<OutOrderTaskMain>().eq("out_order_id", outOrder.getId()).eq("execute_staff", WarehouseConstant.OutWarehouseTaskStaffEnum.TALLY.getCode()));
        } else if (WarehouseConstant.RoleCodeEnum.DRIVER.getCode().equals(sysRole.getRoleCode())) {
            Assert.isTrue(Arrays.asList(WarehouseConstant.EnterWarehouseTaskStaffEnum.DRIVER.getCode(), WarehouseConstant.EnterWarehouseTaskStaffEnum.TALLY_DRIVER.getCode()).contains(outOrder.getExecuteStaff()),
                    "当前出库单无需叉车司机处理！");

            outOrder.setDriver(loginUser.getUsername());
            outOrder.setDriverReceive(new Date());

            outOrderTaskMain = outOrderTaskMainService.getOne(new QueryWrapper<OutOrderTaskMain>().eq("out_order_id", outOrder.getId()).eq("execute_staff", WarehouseConstant.OutWarehouseTaskStaffEnum.DRIVER.getCode()));
        }
        Assert.notNull(outOrderTaskMain.getId(), "出库任务信息异常，请联系管理员！");
        Assert.isTrue(WarehouseConstant.OutWarehouseEnum.RELEASE_END.getCode().equals(outOrderTaskMain.getStatus()),
                "出库单当前状态：【" + WarehouseConstant.OutWarehouseEnum.getDefineByCode(outOrder.getStatus()) + "】,不支持领取！");

        // 校验当前登录用户账号下是否有正在执行的任务。业务逻辑：用户账号不允许更改
        QueryWrapper<OutOrderTaskMain> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("execute_user_name", loginUser.getUsername());
        queryWrapper.eq("status", WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode());
        int doingCount = outOrderTaskMainService.count(queryWrapper);
        Assert.isTrue(doingCount == 0, "您还有未执行完成的任务，请执行任务后再领取新的任务");

        // 领取任务 更新状态
        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode());
        outOrderTaskMain.setExecuteUserName(loginUser.getUsername());
        outOrderTaskMain.setStatus(WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode());
        outOrderService.updateById(outOrder);
        outOrderTaskMainService.updateById(outOrderTaskMain);

        // 呼叫货架
        if (WarehouseConstant.RoleCodeEnum.TALLY.getCode().equals(sysRole.getRoleCode())) {
            Set<Object> taskSet = redisUtil.sGet(RedisUtil.TASK_LIST_KEY + stationCode);
            Assert.isTrue(CollectionUtils.isEmpty(taskSet), "请放行所有货架后领取出库单！");

            List<OutOrderTaskDetail> outOrderTaskDetailList = outOrderTaskDetailService.list(new QueryWrapper<OutOrderTaskDetail>()
                    .eq("out_order_task_id", outOrderTaskMain.getId()));
            // 线程池
//            threadPoolManager.executeTask(() -> {
            // 筛选出需要呼叫的货架
            Set<String> needCallShelfCodeList = outOrderTaskDetailList.stream().map(OutOrderTaskDetail::getShelfCode).collect(Collectors.toSet());
            for (String shelfCode : needCallShelfCodeList) {
                // 查询当前货架需要出库的货格集合(根据货格决定要呼叫哪一面)
                long haveFrontCount = outOrderTaskDetailList.stream().filter(data -> data.getShelfCode().equals(shelfCode) && data.getCompartmentCode().contains("F")).count();
                String shelfFace = haveFrontCount > 0 ? "F" : "B";

                // 调用RMS呼叫货架
                JSONObject jsonObject = rmsUtil.deliverShelfToStation(Integer.parseInt(stationCode), shelfCode, Collections.singletonList(shelfFace));
                // 判断请求结果
                int code = jsonObject.getJSONObject("response").getJSONObject("header").getIntValue("code");
                String msg = jsonObject.getJSONObject("response").getJSONObject("header").getString("msg");
                Assert.isTrue(code == 0, msg);

                // 获取 taskId
                int taskId = jsonObject.getJSONObject("response").getJSONObject("body").getIntValue("taskId");
                // 任务数据存入数据库(后续不查，这个表只用于备份调用记录)
                ShelfTask newShelfTask = new ShelfTask();
                newShelfTask.setTaskId(taskId);
                newShelfTask.setStationCode(stationCode);
                newShelfTask.setShelfCode(shelfCode);
                newShelfTask.setShelfFace(shelfFace);
                shelfTaskService.save(newShelfTask);
                // 货架朝向存入Redis(String)
                boolean createFace = redisUtil.set(RedisUtil.STATION_CODE + stationCode + RedisUtil.FACE + shelfCode, shelfFace);
                Assert.isTrue(createFace, "保存货架朝向失败！请联系管理员！");
                // 货架编码存入Redis中的工作站任务列表中(Set)
                long createCount = redisUtil.sSet(RedisUtil.TASK_LIST_KEY + stationCode, shelfCode);
                Assert.isTrue(createCount == 1, "保存任务失败！请联系管理员！");
            }
//            });
        }

        return Result.OK("领取成功！");
    }


    /**
     * 查询当前工作站的入库任务及任务货架
     *
     * @param stationCode
     * @return
     */
    @AutoLog(value = "出库操作-刷新当前货架")
    @ApiOperation(value = "出库操作-刷新当前货架", notes = "出库操作-刷新当前货架")
    @PostMapping(value = "/getOutOrderShelf")
    public Result<?> getOutOrderShelf(String stationCode, String outOrderId) {
        Assert.isTrue(StringUtils.isNotBlank(stationCode), "缺少必要参数工作站编码！");

        // 通过Redis缓存获取货架朝向并调用RMS获取货架编码、任务信息
        ShelfTask shelfTask = this.getShelfTaskForRMS(stationCode);

        ShelfPTO response = new ShelfPTO();
        // 封装需要返回给前端的数据：货架朝向
        response.setShelfFace(shelfTask.getShelfFace());
        // 封装需要返回给前端的数据：货架对象
        BasicShelf shelfInfo = shelfService.getOne(new QueryWrapper<BasicShelf>().eq("shelf_code", shelfTask.getShelfCode()));
        response.setBasicShelf(shelfInfo);
        // 封装需要返回给前端的数据：货架类型对象
        BasicShelfType shelfTypeInfo = shelfTypeService.getOne(new QueryWrapper<BasicShelfType>().eq("shelf_code", shelfInfo.getShelfTypeCode()));
        response.setBasicShelfType(shelfTypeInfo);
        // 封装需要返回给前端的数据：货格集合
        List<BasicCompartment> compartmentList = basicCompartmentService.list(new LambdaQueryWrapper<BasicCompartment>()
                .eq(BasicCompartment::getShelfCode, shelfTask.getShelfCode())
                .eq(BasicCompartment::getIsTwoSided, shelfTask.getShelfFace())
                .orderByAsc(BasicCompartment::getCompartmentCode));
        response.setCompartmentList(compartmentList);
        // 封装需要返回给前端的数据：当前货架朝向需要出库的并且没做完的任务详情集合
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getOutOrderId, outOrderId)
                .eq(OutOrderTaskMain::getExecuteUserName, loginUser.getUsername()));
        if (null != outOrderTaskMain) {
            List<OutOrderTaskDetail> outOrderTaskDetailList = outOrderTaskDetailService.list(new LambdaQueryWrapper<OutOrderTaskDetail>()
                    .eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId())
                    .eq(OutOrderTaskDetail::getShelfCode, shelfTask.getShelfCode()));
            if (CollectionUtils.isNotEmpty(outOrderTaskDetailList)) {
                outOrderTaskDetailList = outOrderTaskDetailList.stream().filter(data -> data.getCompartmentCode().contains(shelfTask.getShelfFace())).collect(Collectors.toList());
            }
            response.setOutOrderTasks(outOrderTaskDetailList);
        }

        return Result.OK(response);
    }

    /**
     * 从RMS查询当前工作站的货架与任务信息
     *
     * @param stationCode
     * @return
     */
    public ShelfTask getShelfTaskForRMS(String stationCode) {
        // 查询工作站信息
        BasicStationInfo stationInfo = stationService.getOne(new LambdaQueryWrapper<BasicStationInfo>().eq(BasicStationInfo::getStationCode, stationCode));

        // 查询当前工作站点位的货架编码是什么(接口不返回任务id)
        JSONObject shelfCodeResponse = rmsUtil.selectShelfCodeByStationPoint(Collections.singletonList(stationInfo.getStationPoint()));
        int shelfCodeResponseCode = shelfCodeResponse.getJSONObject("response").getJSONObject("header").getIntValue("code");
        Assert.isTrue(shelfCodeResponseCode == 0, "请确认货架放置在工作站点位后刷新货架信息！若问题持续存在，请联系管理员！");

        JSONArray shelfList = shelfCodeResponse.getJSONObject("response").getJSONObject("body").getJSONArray("shelves");
        Assert.notEmpty(shelfList, "未查询到工作站点位的货架信息！请联系管理员！");
        JSONObject shelf = JSON.parseObject(JSONObject.toJSONString(shelfList.get(0)));
        // 当前位置的货架编码
        String shelfCode = shelf.getString("shelfCode");
        // 机器人id(货架在哪个机器人上)
        String robotId = shelf.getString("robotId");
        Assert.isTrue(StringUtils.isNotBlank(shelfCode), "未查询到工作站点位的货架编码，请联系管理员！");
        Assert.isTrue(StringUtils.isNotBlank(robotId), "未查询到货架下的机器人信息，请联系管理员！");

        // 由于上一个接口没有获取到任务id，所以要通过机器人id获取到任务id(用于后续转面和放行)
        JSONObject taskResponse = rmsUtil.selectTaskIdByRobotId(robotId);
        int taskResponseCode = taskResponse.getJSONObject("response").getJSONObject("header").getIntValue("code");
        String taskResponseMessage = taskResponse.getJSONObject("response").getJSONObject("header").getString("msg");
        Assert.isTrue(taskResponseCode == 0, taskResponseMessage);
        JSONArray robotList = taskResponse.getJSONObject("response").getJSONObject("body").getJSONArray("robots");
        Assert.notEmpty(robotList, "未查询到工作站点位的机器人信息！请联系管理员！");
        JSONObject robot = JSON.parseObject(JSONObject.toJSONString(robotList.get(0)));
        // 任务id
        Integer taskId = robot.getInteger("taskId");
        Assert.notNull(taskId, "未查询到机器人的任务id，请联系管理员！");

        // 获取货架朝向(通过Redis获取，呼叫货架时已存储货架的朝向)
        String shelfFace = (String) redisUtil.get(RedisUtil.STATION_CODE + stationCode + RedisUtil.FACE + shelfCode);
        Assert.notNull(shelfFace, "请等待货架运送至工作站点位后刷新货架信息！若问题持续存在，请联系管理员！");

        // 将任务信息存入Redis中(String)
        ShelfTask shelfTask = new ShelfTask();
        shelfTask.setTaskId(taskId);
        shelfTask.setStationCode(stationCode);
        shelfTask.setShelfCode(shelfCode);
        shelfTask.setShelfFace(shelfFace);
        redisUtil.set(RedisUtil.DOING_TASK_KEY + stationCode, shelfTask);
        return shelfTask;
    }

    /**
     * 当前登录人正在执行的任务
     *
     * @param
     * @return
     */
    @GetMapping("/getDoingOrder")
    public Result<?> getDoingOrder() {
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        // 正常情况下 list中应有一条数据 如果没有数据代表当前登录人没有正在执行的任务，如果数据大于1条代表同时接了多个任务。
        List<OutOrderTaskMain> list = outOrderTaskMainService.list(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getExecuteUserName, loginUser.getUsername())
                .eq(OutOrderTaskMain::getStatus, WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()));
        if (CollectionUtils.isEmpty(list)) {
            return Result.OK(new InOrder());
        }

        OutOrder inOrder = outOrderService.getById(list.get(0).getOutOrderId());
        return Result.OK(inOrder);
    }

    /**
     * 理货员
     * 出库至换装区
     *
     * @param tagNumber
     * @return
     */
    @AutoLog(value = "出库操作-扫码出库")
    @ApiOperation(value = "出库操作-扫码出库", notes = "出库操作-扫码出库")
    @PostMapping(value = "/outOrderOperation")
    public Result<?> outOrderOperation(@RequestParam(name = "tagNumber") String tagNumber,
                                       @RequestParam(name = "compartmentCode") String compartmentCode,
                                       @RequestBody OutOrder outOrder) {
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();

        String labelNumber = tagNumber;
        OutOrder order = outOrderService.getById(outOrder.getId());
        Assert.isTrue(order.getStatus().equals(WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()), "出库单非进行中状态，无法出库，请联系管理员！");

        // 获取本次要入库的品番
        String skuCode = null;
        List<OutOrderDetail> outOrderDetailList = outOrderDetailService.list(new LambdaQueryWrapper<OutOrderDetail>().eq(OutOrderDetail::getOutOrderId, order.getId()));
        Assert.notEmpty(outOrderDetailList, "出库单详情数据异常，请联系管理员！");
        outOrderDetailList.sort(Comparator.comparingInt((OutOrderDetail o) -> o.getSkuCode().length()).reversed());
        for (OutOrderDetail outOrderDetail : outOrderDetailList) {
            if (labelNumber.contains(outOrderDetail.getSkuCode())) {
                skuCode = outOrderDetail.getSkuCode();
                break;
            }
        }
        Assert.notNull(skuCode, "请扫描当前任务中的品番");
        BasicSkuInfo basicSkuInfo = basicSkuInfoService.list(new LambdaQueryWrapper<BasicSkuInfo>().eq(BasicSkuInfo::getSkuCode, skuCode)).get(0);

        OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getOutOrderId, order.getId())
                .eq(OutOrderTaskMain::getExecuteUserName, loginUser.getUsername())
                .eq(OutOrderTaskMain::getStatus, WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()));
        Assert.notNull(outOrderTaskMain, "您还未领取任务");
        OutOrderTaskDetail outOrderTaskDetail = outOrderTaskDetailService.getOne(new LambdaQueryWrapper<OutOrderTaskDetail>()
                .eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId())
                .eq(OutOrderTaskDetail::getCompartmentCode, compartmentCode)
                .eq(OutOrderTaskDetail::getSkuCode, skuCode));
        Assert.notNull(outOrderTaskDetail, "请按照系统提示扫码出库");

        Integer needOutBoxQuantity = outOrderTaskDetail.getNeedBoxQuantity(); // 应出库数量
        Integer endOutBoxQuantity = outOrderTaskDetail.getEndBoxQuantity();
        Assert.isTrue(needOutBoxQuantity > endOutBoxQuantity, "请勿多出");

        // 查询扫描的品番它的在库信息
        ReportLabel reportLabel = reportLabelService.getOne(new LambdaQueryWrapper<ReportLabel>()
                .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.AGV.getCode())
                .eq(ReportLabel::getLabelNumber, tagNumber)
                .eq(ReportLabel::getSkuCode, skuCode)
                .eq(ReportLabel::getCompartmentCode, compartmentCode));
        Assert.notNull(reportLabel, "货格【%s】内不存在当前扫描的标签号！", compartmentCode);
        Integer availableBoxQuantity = reportLabel.getAvailableBoxQuantity(); // 这次扫码可以出库的物品数量
        Assert.isTrue(availableBoxQuantity > 0, "该标签号已经出库完成了，库存中没有这个物品了！");

        Integer outGoodsQuantity = basicSkuInfo.getBoxCapacity(); // 本次出库物品数量

        // 更新出库任务详情的已出库数量
        outOrderTaskDetail.setEndBoxQuantity(outOrderTaskDetail.getEndBoxQuantity() + 1);
        outOrderTaskDetail.setEndGoodsQuantity(outOrderTaskDetail.getEndGoodsQuantity() + outGoodsQuantity);
        outOrderTaskDetail.setStatus(Objects.equals(outOrderTaskDetail.getNeedBoxQuantity(), outOrderTaskDetail.getEndBoxQuantity()) ? WarehouseConstant.OutWarehouseDetailEnum.OUT_END.getCode() : outOrderTaskDetail.getStatus());
        outOrderTaskDetailService.updateById(outOrderTaskDetail);

        // ReportLabel(AGV区出库)
        reportLabel.setAvailableBoxQuantity(reportLabel.getAvailableBoxQuantity() - 1);
        reportLabel.setAvailableGoodsQuantity(reportLabel.getAvailableGoodsQuantity() - outGoodsQuantity);
        reportLabelService.updateById(reportLabel);
        // ReportLabel(换装区入库)
        ReportLabel lsReportLabel = reportLabelService.getOne(new LambdaQueryWrapper<ReportLabel>()
                .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                .eq(ReportLabel::getLabelNumber, labelNumber)
                .eq(ReportLabel::getSkuCode, skuCode)
                .eq(ReportLabel::getInOrderId, reportLabel.getInOrderId())
                .eq(ReportLabel::getOutOrderId, outOrder.getId()));
        if (null == lsReportLabel) {
            lsReportLabel = new ReportLabel();
            lsReportLabel.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode());
            lsReportLabel.setLabelNumber(reportLabel.getLabelNumber());
            lsReportLabel.setSkuCode(skuCode);
            lsReportLabel.setInventoryBoxQuantity(1);
            lsReportLabel.setInventoryGoodsQuantity(outGoodsQuantity);
            lsReportLabel.setAvailableBoxQuantity(1);
            lsReportLabel.setAvailableGoodsQuantity(outGoodsQuantity);
            lsReportLabel.setShelfCode("-");
            lsReportLabel.setCompartmentCode("-");
            lsReportLabel.setSkuType(reportLabel.getSkuType());
            lsReportLabel.setInOrderId(reportLabel.getInOrderId());
            lsReportLabel.setInOrderCode(reportLabel.getInOrderCode());
            lsReportLabel.setInDate(reportLabel.getInDate());
            lsReportLabel.setStackingDate(new Date());
            lsReportLabel.setContainerNo(reportLabel.getContainerNo());
            lsReportLabel.setOutOrderId(outOrder.getId());
            reportLabelService.save(lsReportLabel);
        } else {
            lsReportLabel.setInventoryBoxQuantity(lsReportLabel.getInventoryBoxQuantity() + 1);
            lsReportLabel.setInventoryGoodsQuantity(lsReportLabel.getInventoryGoodsQuantity() + outGoodsQuantity);
            lsReportLabel.setAvailableBoxQuantity(lsReportLabel.getAvailableBoxQuantity() + 1);
            lsReportLabel.setAvailableGoodsQuantity(lsReportLabel.getAvailableGoodsQuantity() + outGoodsQuantity);
            reportLabelService.updateById(lsReportLabel);
        }

        // inventory_info(AGV区出库)
        InventoryInfo inventoryInfo = inventoryInfoService.getOne(new LambdaQueryWrapper<InventoryInfo>()
                .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.AGV.getCode())
                .eq(InventoryInfo::getShelfCode, outOrderTaskDetail.getShelfCode())
                .eq(InventoryInfo::getCompartmentCode, outOrderTaskDetail.getCompartmentCode())
                .eq(InventoryInfo::getSkuCode, skuCode));
        Assert.notNull(inventoryInfo, "库存数据异常，请联系管理员！");
        inventoryInfo.setInventoryBoxQuantity(inventoryInfo.getInventoryBoxQuantity() - 1);
        inventoryInfo.setInventoryGoodsQuantity(inventoryInfo.getInventoryGoodsQuantity() - outGoodsQuantity);
        if (inventoryInfo.getInventoryBoxQuantity() == 0 && inventoryInfo.getInventoryGoodsQuantity() == 0 && inventoryInfo.getAvailableBoxQuantity() == 0) {
            inventoryInfoService.removeById(inventoryInfo.getId()); // 如果库存箱数、库存件数、可用库存箱数都是0,删除这条数据
        } else {
            inventoryInfoService.updateById(inventoryInfo);
        }
        // inventory_info(换装区入库)
        InventoryInfo lsInventoryInfo = inventoryInfoService.getOne(new LambdaQueryWrapper<InventoryInfo>()
                .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                .eq(InventoryInfo::getSkuCode, skuCode)
                .eq(InventoryInfo::getOutOrderId, outOrder.getId()));
        if (null == lsInventoryInfo) {
            lsInventoryInfo = new InventoryInfo();
            lsInventoryInfo.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode());
            lsInventoryInfo.setShelfCode("-");
            lsInventoryInfo.setCompartmentCode("-");
            lsInventoryInfo.setSkuCode(skuCode);
            lsInventoryInfo.setInventoryBoxQuantity(1);
            lsInventoryInfo.setInventoryGoodsQuantity(outGoodsQuantity);
            lsInventoryInfo.setAvailableBoxQuantity(1);
            lsInventoryInfo.setAvailableGoodsQuantity(outGoodsQuantity);
            lsInventoryInfo.setEnterDate(new Date());
            lsInventoryInfo.setOutOrderId(outOrder.getId());
            inventoryInfoService.save(lsInventoryInfo);
        } else {
            lsInventoryInfo.setInventoryBoxQuantity(lsInventoryInfo.getInventoryBoxQuantity() + 1);
            lsInventoryInfo.setInventoryGoodsQuantity(lsInventoryInfo.getInventoryGoodsQuantity() + outGoodsQuantity);
            lsInventoryInfo.setAvailableBoxQuantity(lsInventoryInfo.getAvailableBoxQuantity() + 1);
            lsInventoryInfo.setAvailableGoodsQuantity(lsInventoryInfo.getAvailableGoodsQuantity() + outGoodsQuantity);
            inventoryInfoService.updateById(lsInventoryInfo);
        }

        // 保存出库记录
        OutOrderRecord outOrderRecord = outOrderRecordService.getOne(new LambdaQueryWrapper<OutOrderRecord>()
                .eq(OutOrderRecord::getOutOrderId, order.getId())
                .eq(OutOrderRecord::getReportId, reportLabel.getId()));
        if (null == outOrderRecord) {
            outOrderRecord = new OutOrderRecord();
            outOrderRecord.setOutOrderId(order.getId());
            outOrderRecord.setReportId(reportLabel.getId());
            outOrderRecord.setOutBoxNum(1);
            outOrderRecordService.save(outOrderRecord);
        } else {
            outOrderRecord.setOutBoxNum(outOrderRecord.getOutBoxNum() + 1);
            outOrderRecordService.updateById(outOrderRecord);
        }

        int notOutCount = outOrderTaskDetailService.count(new LambdaQueryWrapper<OutOrderTaskDetail>()
                .eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId())
                .eq(OutOrderTaskDetail::getStatus, WarehouseConstant.OutWarehouseDetailEnum.OUT_NOT.getCode()));
        if (notOutCount == 0) {
            outOrderTaskMain.setStatus(WarehouseConstant.OutWarehouseEnum.EXECUTE_END.getCode());
            outOrderTaskMainService.updateById(outOrderTaskMain);
        }

        // 当前这个任务详情完成之后并且只有AGV执行任务。校验任务状态是否需要更改为换装中
        int count = outOrderTaskMainService.count(new LambdaQueryWrapper<OutOrderTaskMain>()
                .ne(OutOrderTaskMain::getId, outOrderTaskMain.getId())
                .eq(OutOrderTaskMain::getOutOrderId, outOrder.getId()));
        if (needOutBoxQuantity - endOutBoxQuantity == 1 && count == 0) {
            // 登录人的未完成任务详情数量
            int notOutEndCount = outOrderTaskDetailService.count(new LambdaQueryWrapper<OutOrderTaskDetail>()
                    .eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId())
                    .eq(OutOrderTaskDetail::getStatus, WarehouseConstant.OutWarehouseDetailEnum.OUT_NOT.getCode()));
            if (notOutEndCount == 0) { // 登录人的出库任务已完成。
                // 更新出库任务状态为：执行完成/换装中
                outOrderTaskMain.setStatus(WarehouseConstant.OutWarehouseEnum.EXECUTE_END.getCode());
                outOrderTaskMainService.updateById(outOrderTaskMain);

                // 更新出库单状态为：执行完成/换装中
                order.setTallyTime(new Date());
                order.setStatus(WarehouseConstant.OutWarehouseEnum.EXECUTE_END.getCode());
                outOrderService.updateById(order);
            }
        }
        return Result.OK();
    }

    /**
     * 货架放行
     *
     * @param stationCode 工作站编码
     * @return
     */
    @AutoLog(value = "出库操作-货架放行")
    @ApiOperation(value = "出库操作-货架放行", notes = "出库操作-货架放行")
    @GetMapping("/releaseShelf/{stationCode}/{outOrderId}")
    public Result<?> releaseShelf(@PathVariable("stationCode") String stationCode, @PathVariable("outOrderId") String outOrderId) {
        Assert.isTrue(StringUtils.isNotBlank(stationCode), "缺少必要参数工作站编码！");

        // 获取正在执行的任务
        Object shelfTaskObject = redisUtil.get(RedisUtil.DOING_TASK_KEY + stationCode);
        ShelfTask shelfTask = (ShelfTask) shelfTaskObject;
        Assert.notNull(shelfTask, "货架已放行，请勿重复操作！");

        // 调用RMS放行
        JSONObject jsonObject = rmsUtil.deliverShelfToPlacement(shelfTask.getTaskId());
        int code = jsonObject.getJSONObject("response").getJSONObject("header").getIntValue("code");
        String msg = jsonObject.getJSONObject("response").getJSONObject("header").getString("msg");
        Assert.isTrue(code == 0, msg);

        // 任务已执行完毕 删除当前任务、删除货架朝向
        redisUtil.del(RedisUtil.DOING_TASK_KEY + stationCode, RedisUtil.STATION_CODE + stationCode + RedisUtil.FACE + shelfTask.getShelfCode());
        // 任务已执行完毕 删除工作站任务列表中的该货架编码
        redisUtil.setRemove(RedisUtil.TASK_LIST_KEY + stationCode, shelfTask.getShelfCode());

        // 放行之后判断这个货架的背面是否存在需要出库的货物
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getOutOrderId, outOrderId)
                .eq(OutOrderTaskMain::getExecuteUserName, loginUser.getUsername())
                .eq(OutOrderTaskMain::getStatus, WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()));
        // 如果outOrderTaskMain代表没有正在出库中的任务，无需查询
        if (null != outOrderTaskMain && shelfTask.getShelfFace().equals("F")) {
            // 查询当前任务详情中需要出库的这个货架的B面货格的数量
            List<OutOrderTaskDetail> outOrderTaskDetailList = outOrderTaskDetailService.list(new QueryWrapper<OutOrderTaskDetail>()
                    .eq("out_order_task_id", outOrderTaskMain.getId())
                    .eq("shelf_code", shelfTask.getShelfCode()));
            long haveBackCount = outOrderTaskDetailList.stream().filter(data -> data.getCompartmentCode().contains("B")).count();
            if (haveBackCount > 0) {
                // 呼叫货架
                JSONObject callShelfResponse = rmsUtil.deliverShelfToStation(Integer.parseInt(stationCode), shelfTask.getShelfCode(), Collections.singletonList("B"));
                // 判断请求结果
                int callShelfResponseCode = callShelfResponse.getJSONObject("response").getJSONObject("header").getIntValue("code");
                String callShelfResponseMessage = callShelfResponse.getJSONObject("response").getJSONObject("header").getString("msg");
                Assert.isTrue(callShelfResponseCode == 0, callShelfResponseMessage);

                // 获取 taskId
                int taskId = callShelfResponse.getJSONObject("response").getJSONObject("body").getIntValue("taskId");
                // 任务数据存入数据库(后续不查，这个表只用于备份调用记录)
                ShelfTask newShelfTask = new ShelfTask();
                newShelfTask.setTaskId(taskId);
                newShelfTask.setStationCode(stationCode);
                newShelfTask.setShelfCode(shelfTask.getShelfCode());
                newShelfTask.setShelfFace("B");
                shelfTaskService.save(newShelfTask);
                // 货架朝向存入Redis(String)
                boolean createFace = redisUtil.set(RedisUtil.STATION_CODE + stationCode + RedisUtil.FACE + shelfTask.getShelfCode(), "B");
                Assert.isTrue(createFace, "保存货架朝向失败！请联系管理员！");
                // 货架编码存入Redis中的工作站任务列表中(Set)
                long createCount = redisUtil.sSet(RedisUtil.TASK_LIST_KEY + stationCode, shelfTask.getShelfCode());
                Assert.isTrue(createCount == 1, "保存任务失败！请联系管理员！");
            }

        }

        return Result.OK();
    }

    /**
     * 查看要执行的任务(叉车司机)
     *
     * @param
     * @return
     */
    @AutoLog(value = "出库管理-叉车司机查看任务列表")
    @ApiOperation(value = "出库管理-叉车司机查看任务列表", notes = "出库管理-叉车司机查看任务列表")
    @PostMapping(value = "/getDoingTask")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> getDoingTask() {
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        SysRole sysRole = sysUserService.selectLoginUserRole();
        Assert.isTrue(Objects.equals(WarehouseConstant.RoleCodeEnum.DRIVER.getCode(), sysRole.getRoleCode()), "您不是叉车司机，无正在执行中的叉车司机任务！");


        OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getExecuteUserName, sysUser.getUsername())
                .eq(OutOrderTaskMain::getStatus, WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()));
        Assert.notNull(outOrderTaskMain, "当前登录人没有正在执行中的出库任务！");

        List<OutOrderTaskDetail> outOrderTaskDetailList = outOrderTaskDetailService.list(new LambdaQueryWrapper<OutOrderTaskDetail>().eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId()));
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("id", outOrderTaskMain.getOutOrderId());
        resultMap.put("taskList", outOrderTaskDetailList);
        return Result.OK(resultMap);
    }

    /**
     * 叉车司机
     *
     * @param
     * @return
     */
    @AutoLog(value = "入库操作-叉车司机获取任务详情")
    @ApiOperation(value = "入库操作-叉车司机获取任务详情", notes = "入库操作-叉车司机获取任务详情")
    @PostMapping(value = "/getDriverTaskDetailList")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> getDriverTaskDetailList(@RequestParam(name = "id") String outOrderId) {
        OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getOutOrderId, outOrderId)
                .eq(OutOrderTaskMain::getExecuteStaff, WarehouseConstant.EnterWarehouseTaskStaffEnum.DRIVER.getCode()));
        Assert.notNull(outOrderTaskMain, "任务数据异常，请联系管理员！");
        // 求本次扫码的品番需要入库的数量
        List<OutOrderTaskDetail> outOrderTaskDetailList = outOrderTaskDetailService.list(new LambdaQueryWrapper<OutOrderTaskDetail>().eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId()));
        outOrderTaskDetailList = outOrderTaskDetailList.stream().sorted(Comparator.comparing(OutOrderTaskDetail::getStatus)).collect(Collectors.toList());
        return Result.OK(outOrderTaskDetailList);
    }

    /**
     * 扫码出库
     *
     * @param
     * @return
     */
    @AutoLog(value = "出库管理-叉车司机扫码出库")
    @ApiOperation(value = "出库管理-叉车司机扫码出库", notes = "出库管理-叉车司机扫码出库")
    @PostMapping(value = "/driverScanQRCode")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> driverScanQRCode(@RequestBody JSONObject jsonObject) {
        String outOrderId = jsonObject.getString("outOrderId");
        String labelNumber = jsonObject.getString("labelNumber");
        String comportmentCode = jsonObject.getString("comportmentCode");
        Assert.isTrue(StringUtils.isNotBlank(outOrderId), "缺少必要参数outOrderId！");
        Assert.isTrue(StringUtils.isNotBlank(labelNumber), "缺少必要参数labelNumber！");
        Assert.isTrue(StringUtils.isNotBlank(comportmentCode), "缺少必要参数comportmentCode！");

        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();

        OutOrder outOrder = outOrderService.getById(outOrderId);
        Assert.notNull(outOrder, "出库单信息异常，请联系管理员！");
        Assert.isTrue(Objects.equals(WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode(), outOrder.getStatus()), "出库单非进行中状态，无法出库，请联系管理员！");

        OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getOutOrderId, outOrderId)
                .eq(OutOrderTaskMain::getExecuteUserName, sysUser.getUsername())
                .eq(OutOrderTaskMain::getStatus, WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()));
        Assert.notNull(outOrderTaskMain, "出库单任务异常，请联系管理员！");

        List<OutOrderTaskDetail> outOrderTaskDetailList = outOrderTaskDetailService.list(new LambdaQueryWrapper<OutOrderTaskDetail>()
                .eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId())
                .eq(OutOrderTaskDetail::getCompartmentCode, comportmentCode)
                .eq(OutOrderTaskDetail::getStatus, WarehouseConstant.OutWarehouseDetailEnum.OUT_NOT.getCode()));

        // 获取本次要出库的品番
        Assert.notEmpty(outOrderTaskDetailList, "当前货格已不存在需要出库的品番！");
        long outCount = outOrderTaskDetailList.stream().filter(data -> labelNumber.contains(data.getStackCode())).count();
        Assert.isTrue(outCount > 0, "未查询到对应的货垛！");

        for (OutOrderTaskDetail outOrderTaskDetail : outOrderTaskDetailList) {
            // 找到本次扫码的货垛
            if (labelNumber.contains(outOrderTaskDetail.getStackCode())) {
                BasicSkuInfo skuInfo = basicSkuInfoService.list(new LambdaQueryWrapper<BasicSkuInfo>().eq(BasicSkuInfo::getSkuCode, outOrderTaskDetail.getSkuCode())).get(0);

                // 更新当前货垛的任务
                outOrderTaskDetail.setEndBoxQuantity(outOrderTaskDetail.getNeedBoxQuantity());
                outOrderTaskDetail.setEndGoodsQuantity(outOrderTaskDetail.getNeedBoxQuantity() * skuInfo.getBoxCapacity());
                outOrderTaskDetail.setStatus(WarehouseConstant.OutWarehouseDetailEnum.OUT_END.getCode());
                outOrderTaskDetailService.updateById(outOrderTaskDetail);

                // report_label (高位库出库)
                ReportLabel reportLabel = reportLabelService.getOne(new LambdaQueryWrapper<ReportLabel>()
                        .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.MULTILAYER.getCode())
                        .eq(ReportLabel::getSkuCode, outOrderTaskDetail.getSkuCode())
                        .eq(ReportLabel::getCompartmentCode, outOrderTaskDetail.getCompartmentCode())
                        .eq(ReportLabel::getStackCode, outOrderTaskDetail.getStackCode()));
                reportLabel.setAvailableBoxQuantity(reportLabel.getAvailableBoxQuantity() - outOrderTaskDetail.getNeedBoxQuantity());
                reportLabel.setAvailableGoodsQuantity(reportLabel.getAvailableGoodsQuantity() - outOrderTaskDetail.getNeedBoxQuantity() * skuInfo.getBoxCapacity());
                reportLabelService.updateById(reportLabel);
                // report_label (端数区入库)
                ReportLabel lsReportLabel = new ReportLabel();
                lsReportLabel.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode());
                lsReportLabel.setLabelNumber(labelNumber);
                lsReportLabel.setSkuCode(outOrderTaskDetail.getSkuCode());
                lsReportLabel.setInventoryBoxQuantity(outOrderTaskDetail.getNeedBoxQuantity());
                lsReportLabel.setInventoryGoodsQuantity(outOrderTaskDetail.getNeedBoxQuantity() * skuInfo.getBoxCapacity());
                lsReportLabel.setAvailableBoxQuantity(outOrderTaskDetail.getNeedBoxQuantity());
                lsReportLabel.setAvailableGoodsQuantity(outOrderTaskDetail.getNeedBoxQuantity() * skuInfo.getBoxCapacity());
                lsReportLabel.setShelfCode(outOrderTaskDetail.getShelfCode());
                lsReportLabel.setCompartmentCode(outOrderTaskDetail.getCompartmentCode());
                lsReportLabel.setSkuType(skuInfo.getSkuType());
                lsReportLabel.setInOrderId(reportLabel.getInOrderId());
                lsReportLabel.setInOrderCode(reportLabel.getInOrderCode());
                lsReportLabel.setInDate(reportLabel.getInDate());
                lsReportLabel.setStackingDate(new Date());
                lsReportLabel.setContainerNo(reportLabel.getContainerNo());
                lsReportLabel.setStackCode(outOrderTaskDetail.getStackCode());
                lsReportLabel.setOutOrderId(outOrderId);
                reportLabelService.save(lsReportLabel);

                // inventory_info (高位库出库)
                InventoryInfo inventoryInfo = inventoryInfoService.getOne(new LambdaQueryWrapper<InventoryInfo>()
                        .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.MULTILAYER.getCode())
                        .eq(InventoryInfo::getCompartmentCode, outOrderTaskDetail.getCompartmentCode())
                        .eq(InventoryInfo::getStackCode, outOrderTaskDetail.getStackCode())
                        .eq(InventoryInfo::getSkuCode, outOrderTaskDetail.getSkuCode()));
                inventoryInfo.setInventoryBoxQuantity(inventoryInfo.getInventoryBoxQuantity() - outOrderTaskDetail.getNeedBoxQuantity());
                inventoryInfo.setInventoryGoodsQuantity(inventoryInfo.getInventoryGoodsQuantity() - outOrderTaskDetail.getNeedBoxQuantity() * skuInfo.getBoxCapacity());
                if (inventoryInfo.getInventoryBoxQuantity() == 0 && inventoryInfo.getInventoryGoodsQuantity() == 0 && inventoryInfo.getAvailableBoxQuantity() == 0 && inventoryInfo.getAvailableGoodsQuantity() == 0) {
                    inventoryInfoService.removeById(inventoryInfo.getId());
                } else {
                    inventoryInfoService.updateById(inventoryInfo);
                }

                // inventory_info (端数区入库)
                InventoryInfo lsInventoryInfo = new InventoryInfo();
                lsInventoryInfo.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode());
                lsInventoryInfo.setShelfCode("-");
                lsInventoryInfo.setCompartmentCode("-");
                lsInventoryInfo.setStackCode(outOrderTaskDetail.getStackCode());
                lsInventoryInfo.setSkuCode(outOrderTaskDetail.getSkuCode());
                lsInventoryInfo.setInventoryBoxQuantity(outOrderTaskDetail.getNeedBoxQuantity());
                lsInventoryInfo.setInventoryGoodsQuantity(outOrderTaskDetail.getNeedBoxQuantity() * skuInfo.getBoxCapacity());
                lsInventoryInfo.setAvailableBoxQuantity(outOrderTaskDetail.getNeedBoxQuantity());
                lsInventoryInfo.setAvailableGoodsQuantity(outOrderTaskDetail.getNeedBoxQuantity() * skuInfo.getBoxCapacity());
                lsInventoryInfo.setEnterDate(new Date());
                lsInventoryInfo.setOutOrderId(outOrderId);
                inventoryInfoService.save(lsInventoryInfo);

                OutOrderRecord outOrderRecord = new OutOrderRecord();
                outOrderRecord.setOutOrderId(outOrderId);
                outOrderRecord.setReportId(reportLabel.getId());
                outOrderRecord.setOutBoxNum(outOrderTaskDetail.getNeedBoxQuantity());
                outOrderRecordService.save(outOrderRecord);
            }
        }

        int notOutCount = outOrderTaskDetailService.count(new LambdaQueryWrapper<OutOrderTaskDetail>()
                .eq(OutOrderTaskDetail::getOutOrderTaskId, outOrderTaskMain.getId())
                .eq(OutOrderTaskDetail::getStatus, WarehouseConstant.OutWarehouseDetailEnum.OUT_NOT.getCode()));
        if (notOutCount == 0) {
            outOrderTaskMain.setStatus(WarehouseConstant.OutWarehouseEnum.EXECUTE_END.getCode());
            outOrderTaskMainService.updateById(outOrderTaskMain);
        }

        return Result.OK(outOrderTaskMain.getStatus());
    }

    /**
     * 搬运接泊位至市贩品出库工作站
     *
     * @return
     */
    @AutoLog(value = "出库管理-点对点搬运")
    @ApiOperation(value = "出库管理-点对点搬运", notes = "出库管理-点对点搬运")
    @PostMapping(value = "/pointToPointTransport")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> pointToPointTransport() {
        String stationCode = "1";
        String shelfCode = "00000151";
        String shelfFace = "F";

        // 调用RMS呼叫货架
        JSONObject jsonObject = rmsUtil.deliverShelfToStation(Integer.parseInt(stationCode), shelfCode, Collections.singletonList(shelfFace));
        // 判断请求结果
        int code = jsonObject.getJSONObject("response").getJSONObject("header").getIntValue("code");
        String msg = jsonObject.getJSONObject("response").getJSONObject("header").getString("msg");
        Assert.isTrue(code == 0, msg);

        // 获取 taskId
        int taskId = jsonObject.getJSONObject("response").getJSONObject("body").getIntValue("taskId");
        // 任务数据存入数据库(后续不查，这个表只用于备份调用记录)
        ShelfTask newShelfTask = new ShelfTask();
        newShelfTask.setTaskId(taskId);
        newShelfTask.setStationCode(stationCode);
        newShelfTask.setShelfCode(shelfCode);
        newShelfTask.setShelfFace(shelfFace);
        shelfTaskService.save(newShelfTask);

        // 货架朝向存入Redis(String)
        boolean createFace = redisUtil.set(RedisUtil.STATION_CODE + stationCode + RedisUtil.FACE + shelfCode, shelfFace);
        Assert.isTrue(createFace, "保存货架朝向失败！请联系管理员！");
        // 货架编码存入Redis中的工作站任务列表中(Set)
        long createCount = redisUtil.sSet(RedisUtil.TASK_LIST_KEY + stationCode, shelfCode);
        Assert.isTrue(createCount == 1, "保存任务失败！请联系管理员！");

        return Result.OK();
    }

    /**
     * 合单(理货员收到出库单所有品番后确认合单)
     *
     * @param outOrderId
     * @return
     */
    @AutoLog(value = "出库管理-合单")
    @ApiOperation(value = "出库管理-合单", notes = "出库管理-合单")
    @PostMapping(value = "/mergeOrder")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> mergeOrder(@RequestParam(name = "id", required = true) String outOrderId) {
        OutOrder outOrder = outOrderService.getById(outOrderId);
        Assert.notNull(outOrder, "出库单数据异常，请联系管理员！");
        Assert.isTrue(Objects.equals(outOrder.getStatus(), WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()), "只有正在执行中的出库单才需要合单，当前出库单状态无需合单！");

        int count = outOrderTaskMainService.count(new LambdaQueryWrapper<OutOrderTaskMain>()
                .eq(OutOrderTaskMain::getOutOrderId, outOrderId)
                .in(OutOrderTaskMain::getStatus, Arrays.asList(WarehouseConstant.OutWarehouseEnum.RELEASE_END.getCode(), WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode())));
        Assert.isTrue(count == 0, "出库单存在未完成的出库任务，无法合单！");

        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.EXECUTE_END.getCode());
        outOrderService.updateById(outOrder);
        return Result.OK();
    }

    /**
     * 换装完成
     * 换装区出库、复核区入库
     *
     * @param id
     * @return
     */
    @AutoLog(value = "出库管理-换装完成")
    @ApiOperation(value = "出库管理-换装完成", notes = "出库管理-换装完成")
    @PostMapping(value = "/convertEnd")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> convertEnd(@RequestParam(name = "id", required = true) String id) {
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();

        OutOrder outOrder = outOrderService.getById(id);
        Assert.notNull(outOrder, "出库单数据异常！请联系管理员！");
        Assert.isTrue(Arrays.asList(WarehouseConstant.OutWarehouseEnum.EXECUTE_END.getCode(), WarehouseConstant.OutWarehouseEnum.FORCE_EXECUTE_END.getCode()).contains(outOrder.getStatus()),
                "出库单当前状态：【" + WarehouseConstant.OutWarehouseEnum.getDefineByCode(outOrder.getStatus()) + "】,无法将出库单状态更新为复核完成！");

        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.EXECUTE_END.getCode().equals(outOrder.getStatus()) ? WarehouseConstant.OutWarehouseEnum.CONVERT_END.getCode() : WarehouseConstant.OutWarehouseEnum.FORCE_CONVERT_END.getCode());
        outOrder.setConvertBy(sysUser.getUsername());
        outOrder.setConvertTime(new Date());
        outOrderService.updateById(outOrder);

        List<OutOrderDetail> outOrderDetailList = outOrderDetailService.list(new LambdaQueryWrapper<OutOrderDetail>().eq(OutOrderDetail::getOutOrderId, id));

        // 更新库存
        Set<String> skuCodeSet = outOrderDetailList.stream().map(OutOrderDetail::getSkuCode).collect(Collectors.toSet());
        List<InventoryInfo> inventoryInfoList = inventoryInfoService.list(new LambdaQueryWrapper<InventoryInfo>()
                .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                .eq(InventoryInfo::getOutOrderId, id));
        List<ReportLabel> reportLabelList = reportLabelService.list(new LambdaQueryWrapper<ReportLabel>()
                .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                .eq(ReportLabel::getOutOrderId, id));
        for (String skuCode : skuCodeSet) {
            BasicSkuInfo skuInfo = basicSkuInfoService.list(new LambdaQueryWrapper<BasicSkuInfo>().eq(BasicSkuInfo::getSkuCode, skuCode)).get(0);
            // 要出库的箱子数量
            int outBoxNumberTotal = outOrderDetailList.stream().filter(data -> skuCode.equals(data.getSkuCode())).mapToInt(OutOrderDetail::getBoxNumber).sum();
            // 要出库的物品数量
            int outGoodsNumberTotal = outOrderDetailList.stream().filter(data -> skuCode.equals(data.getSkuCode())).mapToInt(OutOrderDetail::getGoodsNumber).sum();
            // inventory_info 箱子数量
            int inventoryBoxNumberTotal = inventoryInfoList.stream().filter(data -> skuCode.equals(data.getSkuCode())).mapToInt(InventoryInfo::getAvailableBoxQuantity).sum();
            // inventory_info 物品数量
            int inventoryGoodsNumberTotal = inventoryInfoList.stream().filter(data -> skuCode.equals(data.getSkuCode())).mapToInt(InventoryInfo::getAvailableGoodsQuantity).sum();
            // report_label   箱子数量
            int reportBoxNumberTotal = reportLabelList.stream().filter(data -> skuCode.equals(data.getSkuCode())).mapToInt(ReportLabel::getAvailableBoxQuantity).sum();
            // report_label   物品数量
            int reportGoodsNumberTotal = reportLabelList.stream().filter(data -> skuCode.equals(data.getSkuCode())).mapToInt(ReportLabel::getAvailableGoodsQuantity).sum();
            Assert.isTrue(outBoxNumberTotal == inventoryBoxNumberTotal && outBoxNumberTotal == reportBoxNumberTotal && inventoryGoodsNumberTotal == reportGoodsNumberTotal,
                    "库存数据异常,请联系管理员");

            if (inventoryGoodsNumberTotal > outGoodsNumberTotal) { // AGV区出库的件数有空余
                // 多余件数
                int redundantGoodsQuantity = inventoryGoodsNumberTotal - outGoodsNumberTotal;
                // inventory_info 换装区入库
                InventoryInfo inventoryInfo = inventoryInfoService.getOne(new LambdaQueryWrapper<InventoryInfo>()
                        .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                        .eq(InventoryInfo::getSkuCode, skuCode)
                        .eq(InventoryInfo::getInventoryBoxQuantity, 0)
                        .eq(InventoryInfo::getInventoryGoodsQuantity, 0)
                        .eq(InventoryInfo::getAvailableBoxQuantity, 0)
                        .isNull(InventoryInfo::getOutOrderId));
                if (null == inventoryInfo) {
                    inventoryInfo = new InventoryInfo();
                    inventoryInfo.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode());
                    inventoryInfo.setShelfCode("-");
                    inventoryInfo.setCompartmentCode("-");
                    inventoryInfo.setSkuCode(skuCode);
                    inventoryInfo.setInventoryBoxQuantity(0);
                    inventoryInfo.setInventoryGoodsQuantity(0);
                    inventoryInfo.setAvailableBoxQuantity(0);
                    inventoryInfo.setAvailableGoodsQuantity(redundantGoodsQuantity);
                    inventoryInfo.setEnterDate(new Date());
                    inventoryInfo.setOutOrderId(null);
                    inventoryInfoService.save(inventoryInfo);
                } else {
                    inventoryInfo.setAvailableGoodsQuantity(inventoryInfo.getAvailableGoodsQuantity() + redundantGoodsQuantity);
                    inventoryInfoService.updateById(inventoryInfo);
                }
                // report_label 换装区入库
                ReportLabel reportLabel = reportLabelService.getOne(new LambdaQueryWrapper<ReportLabel>()
                        .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                        .eq(ReportLabel::getSkuCode, skuCode)
                        .eq(ReportLabel::getInventoryBoxQuantity, 0)
                        .eq(ReportLabel::getInventoryGoodsQuantity, 0)
                        .eq(ReportLabel::getAvailableBoxQuantity, 0)
                        .isNull(ReportLabel::getOutOrderId));
                if (null == reportLabel) {
                    reportLabel = new ReportLabel();
                    reportLabel.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode());
                    reportLabel.setLabelNumber(skuCode);
                    reportLabel.setSkuCode(skuCode);
                    reportLabel.setInventoryBoxQuantity(0);
                    reportLabel.setInventoryGoodsQuantity(0);
                    reportLabel.setAvailableBoxQuantity(0);
                    reportLabel.setAvailableGoodsQuantity(redundantGoodsQuantity);
                    reportLabel.setShelfCode("-");
                    reportLabel.setCompartmentCode("-");
                    reportLabel.setSkuType(skuInfo.getSkuType());
                    reportLabel.setInOrderId(null);
                    reportLabel.setInOrderCode(null);
                    reportLabel.setInDate(new Date());
                    reportLabel.setContainerNo("-");
                    reportLabel.setOutOrderId(null);
                    reportLabelService.save(reportLabel);
                } else {
                    reportLabel.setAvailableGoodsQuantity(reportLabel.getAvailableGoodsQuantity() + redundantGoodsQuantity);
                    reportLabelService.updateById(reportLabel);
                }
            } else if (inventoryGoodsNumberTotal < outGoodsNumberTotal) { // AGV区出库的件数不够出
                InventoryInfo inventoryInfo = inventoryInfoService.getOne(new LambdaQueryWrapper<InventoryInfo>()
                        .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                        .eq(InventoryInfo::getSkuCode, skuCode)
                        .eq(InventoryInfo::getInventoryBoxQuantity, 0)
                        .eq(InventoryInfo::getInventoryGoodsQuantity, 0)
                        .eq(InventoryInfo::getAvailableBoxQuantity, 0)
                        .isNull(InventoryInfo::getOutOrderId));
                Assert.notNull(inventoryInfo, "品番编码：【%s】需出库件数：【%s】，换装区库存件数：【%s】，库存不足无法出库！", skuCode, outGoodsNumberTotal, inventoryGoodsNumberTotal);
                ReportLabel reportLabel = reportLabelService.getOne(new LambdaQueryWrapper<ReportLabel>()
                        .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                        .eq(ReportLabel::getSkuCode, skuCode)
                        .eq(ReportLabel::getInventoryBoxQuantity, 0)
                        .eq(ReportLabel::getInventoryGoodsQuantity, 0)
                        .eq(ReportLabel::getAvailableBoxQuantity, 0)
                        .gt(ReportLabel::getAvailableGoodsQuantity, 0)
                        .isNull(ReportLabel::getOutOrderId));
                Assert.notNull(reportLabel, "品番编码：【%s】需出库件数：【%s】，换装区库存件数：【%s】，库存不足无法出库！", skuCode, outGoodsNumberTotal, inventoryGoodsNumberTotal);
                Assert.isTrue(Objects.equals(inventoryInfo.getAvailableGoodsQuantity(), reportLabel.getAvailableGoodsQuantity()), "库存信息异常，请联系管理员！");
                Assert.isTrue(inventoryGoodsNumberTotal + inventoryInfo.getAvailableGoodsQuantity() >= outGoodsNumberTotal, "品番编码：【%s】需出库件数：【%s】，换装区库存件数：【%s】，库存不足无法出库！", skuCode, outGoodsNumberTotal, inventoryGoodsNumberTotal + inventoryInfo.getAvailableGoodsQuantity());

                // 需要从换装区出库的散件数量
                int scatteredNeedOutGoodsQuantity = outGoodsNumberTotal - inventoryGoodsNumberTotal;
                // inventory_info(更新散件数量)
                inventoryInfo.setAvailableGoodsQuantity(inventoryInfo.getAvailableGoodsQuantity() - scatteredNeedOutGoodsQuantity);
                if (inventoryInfo.getAvailableGoodsQuantity() == 0) {
                    inventoryInfoService.removeById(inventoryInfo.getId());
                } else {
                    inventoryInfoService.updateById(inventoryInfo);
                }
                // report_label(更新散件数量)
                reportLabel.setAvailableGoodsQuantity(reportLabel.getAvailableGoodsQuantity() - scatteredNeedOutGoodsQuantity);
                if (reportLabel.getAvailableGoodsQuantity() == 0) {
                    reportLabelService.removeById(reportLabel.getId());
                } else {
                    reportLabelService.updateById(reportLabel);
                }
            }


            // 复核区入库
            Integer boxQuantity = outGoodsNumberTotal / (null == skuInfo.getOutSpecification() ? skuInfo.getBoxCapacity() : skuInfo.getOutSpecification());

            InventoryInfo inventoryInfo = new InventoryInfo();
            inventoryInfo.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.REVIEW.getCode());
            inventoryInfo.setShelfCode("-");
            inventoryInfo.setCompartmentCode("-");
            inventoryInfo.setSkuCode(skuCode);
            inventoryInfo.setInventoryBoxQuantity(boxQuantity);
            inventoryInfo.setInventoryGoodsQuantity(outGoodsNumberTotal);
            inventoryInfo.setAvailableBoxQuantity(boxQuantity);
            inventoryInfo.setAvailableGoodsQuantity(outGoodsNumberTotal);
            inventoryInfo.setEnterDate(new Date());
            inventoryInfo.setOutOrderId(id);
            inventoryInfoService.save(inventoryInfo);
            ReportLabel reportLabel = new ReportLabel();
            reportLabel.setWarehouseCode(WarehouseConstant.WarehouseTypeEnum.REVIEW.getCode());
            reportLabel.setLabelNumber(skuCode);
            reportLabel.setSkuCode(skuCode);
            reportLabel.setInventoryBoxQuantity(boxQuantity);
            reportLabel.setInventoryGoodsQuantity(outGoodsNumberTotal);
            reportLabel.setAvailableBoxQuantity(boxQuantity);
            reportLabel.setAvailableGoodsQuantity(outGoodsNumberTotal);
            reportLabel.setShelfCode("-");
            reportLabel.setCompartmentCode("-");
            reportLabel.setSkuType(skuInfo.getSkuType());
            reportLabel.setInOrderId(null);
            reportLabel.setInOrderCode(null);
            reportLabel.setInDate(new Date());
            reportLabel.setStackingDate(new Date());
            reportLabel.setContainerNo("-");
            reportLabel.setOutOrderId(id);
            reportLabelService.save(reportLabel);
        }

        // 换装区出库
        reportLabelService.update(
                new ReportLabel()
                        .setAvailableBoxQuantity(0)
                        .setAvailableGoodsQuantity(0),
                new LambdaQueryWrapper<ReportLabel>()
                        .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                        .eq(ReportLabel::getOutOrderId, id)
        );

        inventoryInfoService.remove(new LambdaQueryWrapper<InventoryInfo>()
                .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.CONVERT.getCode())
                .eq(InventoryInfo::getOutOrderId, id));

        return Result.OK();
    }

    /**
     * 复核完成
     *
     * @param id
     * @return
     */
    @AutoLog(value = "出库管理-复核完成")
    @ApiOperation(value = "出库管理-复核完成", notes = "出库管理-复核完成")
    @PostMapping(value = "/reviewEnd")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> reviewEnd(@RequestParam(name = "id", required = true) String id) {
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();

        OutOrder outOrder = outOrderService.getById(id);
        Assert.notNull(outOrder, "出库单数据异常！请联系管理员！");
        Assert.isTrue(Arrays.asList(WarehouseConstant.OutWarehouseEnum.CONVERT_END.getCode(), WarehouseConstant.OutWarehouseEnum.FORCE_CONVERT_END.getCode()).contains(outOrder.getStatus()),
                "出库单当前状态：【" + WarehouseConstant.OutWarehouseEnum.getDefineByCode(outOrder.getStatus()) + "】,无法将出库单状态更新为复核完成！");

        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.CONVERT_END.getCode().equals(outOrder.getStatus()) ? WarehouseConstant.OutWarehouseEnum.REVIEW_END.getCode() : WarehouseConstant.OutWarehouseEnum.FORCE_REVIEW_END.getCode());
        outOrder.setReviewBy(sysUser.getUsername());
        outOrder.setReviewTime(new Date());
        outOrderService.updateById(outOrder);

        // 复核区出库
        reportLabelService.update(
                new ReportLabel()
                        .setAvailableBoxQuantity(0)
                        .setAvailableGoodsQuantity(0),
                new LambdaQueryWrapper<ReportLabel>()
                        .eq(ReportLabel::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.REVIEW.getCode())
                        .eq(ReportLabel::getOutOrderId, id)
        );

        inventoryInfoService.remove(new LambdaQueryWrapper<InventoryInfo>()
                .eq(InventoryInfo::getWarehouseCode, WarehouseConstant.WarehouseTypeEnum.REVIEW.getCode())
                .eq(InventoryInfo::getOutOrderId, id));

        return Result.OK();
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    @AutoLog(value = "理货管理-通过id查询")
    @ApiOperation(value = "理货管理-通过id查询", notes = "理货管理-通过id查询")
    @GetMapping(value = "/queryById")
    public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
        OutOrder outOrder = outOrderService.getById(id);
        if (outOrder == null) {
            return Result.error("未找到对应数据");
        }

        SysRole sysRole = sysUserService.selectLoginUserRole();
        if (Objects.equals(WarehouseConstant.RoleCodeEnum.TALLY.getCode(), sysRole.getRoleCode())) {
            OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                    .eq(OutOrderTaskMain::getOutOrderId, outOrder.getId())
                    .eq(OutOrderTaskMain::getExecuteStaff, WarehouseConstant.EnterWarehouseTaskStaffEnum.TALLY.getCode()));
            outOrder.setTaskStatus(null == outOrderTaskMain ? outOrder.getStatus() : outOrderTaskMain.getStatus());
        } else if (Objects.equals(WarehouseConstant.RoleCodeEnum.DRIVER.getCode(), sysRole.getRoleCode())) {
            OutOrderTaskMain outOrderTaskMain = outOrderTaskMainService.getOne(new LambdaQueryWrapper<OutOrderTaskMain>()
                    .eq(OutOrderTaskMain::getOutOrderId, outOrder.getId())
                    .eq(OutOrderTaskMain::getExecuteStaff, WarehouseConstant.EnterWarehouseTaskStaffEnum.DRIVER.getCode()));
            outOrder.setTaskStatus(null == outOrderTaskMain ? outOrder.getStatus() : outOrderTaskMain.getStatus());
        } else {
            outOrder.setTaskStatus(outOrder.getStatus());
        }
        return Result.OK(outOrder);
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    @AutoLog(value = "出库单详情通过主表ID查询")
    @ApiOperation(value = "出库单详情主表ID查询", notes = "出库单详情-通主表ID查询")
    @GetMapping(value = "/queryOutOrderDetailByMainId")
    public Result<?> queryOutOrderDetailListByMainId(@RequestParam(name = "id", required = true) String id) {
        List<OutOrderDetail> list = outOrderDetailService.list(new QueryWrapper<OutOrderDetail>().eq("out_order_id", id));
        return Result.OK(list);
    }

    /**
     * 通过id删除
     *
     * @param id
     * @return
     */
    @AutoLog(value = "出库单-通过id删除")
    @ApiOperation(value = "出库单-通过id删除", notes = "出库单-通过id删除")
    @DeleteMapping(value = "/delete")
    public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
        outOrderService.delMain(id);
        return Result.OK();
    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @AutoLog(value = "出库单-批量删除")
    @ApiOperation(value = "出库单-批量删除", notes = "出库单-批量删除")
    @DeleteMapping(value = "/deleteBatch")
    public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
        this.outOrderService.delBatchMain(Arrays.asList(ids.split(",")));
        return Result.OK("批量删除成功！");
    }

    /**
     * 导出excel
     *
     * @param request
     * @param outOrder
     */
    @RequestMapping(value = "/exportXls")
    public ModelAndView exportXls(HttpServletRequest request, OutOrder outOrder) {
        // Step.1 组装查询条件查询数据
        QueryWrapper<OutOrder> queryWrapper = QueryGenerator.initQueryWrapper(outOrder, request.getParameterMap());
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();

        //Step.2 获取导出数据
        List<OutOrder> queryList = outOrderService.list(queryWrapper);
        // 过滤选中数据
        String selections = request.getParameter("selections");
        List<OutOrder> outOrderList = new ArrayList<OutOrder>();
        if (oConvertUtils.isEmpty(selections)) {
            outOrderList = queryList;
        } else {
            List<String> selectionList = Arrays.asList(selections.split(","));
            outOrderList = queryList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
        }

        // Step.3 组装pageList
        List<OutOrderPage> pageList = new ArrayList<OutOrderPage>();
        for (OutOrder main : outOrderList) {
            OutOrderPage vo = new OutOrderPage();
            BeanUtils.copyProperties(main, vo);
            List<OutOrderDetail> outOrderDetailList = outOrderDetailService.selectByMainId(main.getId());
            vo.setOutOrderDetailList(outOrderDetailList);
            pageList.add(vo);
        }

        // Step.4 AutoPoi 导出Excel
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, "出库单列表");
        mv.addObject(NormalExcelConstants.CLASS, OutOrderPage.class);
        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("出库单数据", "导出人:" + sysUser.getRealname(), "出库单"));
        mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
        return mv;
    }

    /**
     * 强制结束出库单
     * 用于出库数量不够时强制结束
     *
     * @param id
     * @return 响应
     */
    @AutoLog(value = "出库管理-强制结束出库单")
    @ApiOperation(value = "出库管理-强制结束出库单", notes = "出库管理-强制结束出库单")
    @DeleteMapping(value = "/forceEnd")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> forceEnd(String id, String remarks) {
        Assert.isTrue(StringUtils.isNotBlank(id), "缺少必要参数id！");
        Assert.isTrue(StringUtils.isNotBlank(remarks), "请输入强制结束原因！");
//        OutOrder outOrder = outOrderService.getById(id);
//        Assert.notNull(outOrder, "出库单信息异常，请联系管理员！");
//        Assert.isTrue(outOrder.getStatus().equals(WarehouseConstant.OutWarehouseEnum.RECEIVE_END.getCode()), "出库单不是进行中状态，无需强制结束！");
//
//        // 更新出库单
//        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.FORCE_END.getCode());
//        outOrder.setRemarks(outOrder.getRemarks() + "(强制结束原因：" + remarks + ")");
//        outOrderService.updateById(outOrder);
//
//        // 更新出库单详情
//        outOrderDetailService.update(
//                new OutOrderDetail().setStatus(WarehouseConstant.OutWarehouseDetailEnum.OUT_END.getCode()),
//                new QueryWrapper<OutOrderDetail>().eq("out_order_id", id)
//        );
//
//        // 更新出库任务
//        List<OutOrderTaskMain> list = outOrderTaskMainService.list(new QueryWrapper<OutOrderTaskMain>().eq("out_order_id", id));
//        list.forEach(data -> data.setStatus(WarehouseConstant.OutWarehouseEnum.FORCE_END.getCode()));
//        outOrderTaskMainService.updateBatchById(list);
//
//        // 更新出库任务详情
//        outOrderTaskDetailService.update(
//                new OutOrderTaskDetail().setStatus(WarehouseConstant.OutWarehouseDetailEnum.OUT_END.getCode()),
//                new QueryWrapper<OutOrderTaskDetail>().in("out_order_task_id", list.stream().map(OutOrderTaskMain::getId).collect(Collectors.toList()))
//        );

        return Result.OK();
    }

    /**
     * 通过excel导入数据
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            MultipartFile file = entity.getValue();// 获取上传文件对象
            ImportParams params = new ImportParams();
            params.setTitleRows(2);
            params.setHeadRows(1);
            params.setNeedSave(true);
            try {
                List<OutOrderPage> list = ExcelImportUtil.importExcel(file.getInputStream(), OutOrderPage.class, params);
                for (OutOrderPage page : list) {
                    OutOrder po = new OutOrder();
                    BeanUtils.copyProperties(page, po);
                    outOrderService.saveWarehousingOut(po, page.getOutOrderDetailList().stream().filter(data -> StringUtils.isNotBlank(data.getSkuCode())).collect(Collectors.toList()));
                }
                return Result.OK("文件导入成功！数据行数:" + list.size());
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return Result.error("文件导入失败:" + e.getMessage());
            } finally {
                try {
                    file.getInputStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return Result.OK("文件导入失败！");
    }

}
